package vip.liux.front.infrastructure.annotations;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.util.function.SingletonSupplier;
import vip.liux.front.infrastructure.aop.interceptor.IAuditingStore;
import vip.liux.front.infrastructure.utils.AmbientScopeProvider;

import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;

@Configuration(proxyBeanMethods = false)
public abstract class AbstractAuditedConfiguration implements ImportAware {
    @Nullable
    protected AnnotationAttributes enableAudited;

    @Nullable
    protected Supplier<IAuditingStore> auditingStore;

    @Nullable
    protected Supplier<AmbientScopeProvider<?>> ambientScopeProvider;


    @Override
    public void setImportMetadata(AnnotationMetadata importMetadata) {
        this.enableAudited = AnnotationAttributes.fromMap(
                importMetadata.getAnnotationAttributes(EnableAudited.class.getName()));
        if (this.enableAudited == null) {
            throw new IllegalArgumentException(
                    "@EnableAudited is not present on importing class " + importMetadata.getClassName());
        }
    }

    /**
     * Collect any {@link AuditedConfigurer} beans through autowiring.
     */
    @Autowired
    void setConfigurers(ObjectProvider<AuditedConfigurer> configurers) {
        Supplier<AuditedConfigurer> configurer = SingletonSupplier.of(() -> {
            List<AuditedConfigurer> candidates = configurers.stream().toList();
            if (CollectionUtils.isEmpty(candidates)) {
                return null;
            }
            if (candidates.size() > 1) {
                throw new IllegalStateException("Only one AuditedConfigurer may exist");
            }
            return candidates.get(0);
        });
        this.auditingStore = adapt(configurer, AuditedConfigurer::getAuditingStore);
        this.ambientScopeProvider = adapt(configurer, AuditedConfigurer::getAmbientScopeProvider);
    }

    private <T> Supplier<T> adapt(Supplier<AuditedConfigurer> supplier, Function<AuditedConfigurer, T> provider) {
        return () -> {
            AuditedConfigurer configurer = supplier.get();
            return (configurer != null ? provider.apply(configurer) : null);
        };
    }
}
